﻿using Cinemachine;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

namespace XXYFramework
{
    /// <summary>
    /// 玩家角色
    /// </summary>
    [RequireComponent(typeof(Animator), typeof(CharacterController), typeof(AudioSource))]
    public class Character : MonoBehaviour
    {
        [SerializeField] LayerMask obstacleLayer;                   // 障碍物层
        public bool isThirdPerson;                                  // 是否是第三人称
        [SerializeField] bool FirstPersonHideSkinned = true;        // 是否在第一人称模式下隐藏蒙皮
        [Header("虚拟相机")]
        [SerializeField] CinemachineVirtualCamera firstVirCam;      // 第一人称虚拟相机
        [SerializeField] CinemachineFreeLook thirdVirCam;           // 第三人称虚拟相机
        [Header("相机速度")]
        [SerializeField, Range(-2, 2)] float firstCamYOffset = 0;    // 第一人称相机Y轴偏移量
        [SerializeField, Range(5, 50)] float firstCamSpeed = 20;    // 第一人称相机速度
        [SerializeField, Range(36, 360)] float thirdCamSpeed = 360;    // 第三人称相机速度
        [Header("音效")]
        [SerializeField] AudioClip moveClip;        // 移动音效
        [SerializeField] AudioClip jumpClip;        // 跳跃音效
        [SerializeField] AudioClip fallClip;        // 落下音效
        [SerializeField] AudioClip rollClip;        // 翻滚音效

        CharacterController controller;
        PlayerInput input;
        Animator animator;
        AudioSource audioEngine;
        List<SkinnedMeshRenderer> skinnedMeshes = new List<SkinnedMeshRenderer>();
        List<MeshRenderer> meshes = new List<MeshRenderer>();

        /// <summary>
        /// 重力加速值
        /// </summary>
        const float Gravity = -9.8f;
        /// <summary>
        /// 跳跃高度值
        /// </summary>
        const float JumpHeight = 1.5f;
        /// <summary>
        /// 正常行走速度值
        /// </summary>
        const float NormalSpeed = 2;
        /// <summary>
        /// 奔跑 蹲伏速度比值
        /// </summary>
        const float Accelerated = 2.5f;
        /// <summary>
        /// 多少帧没有接触地面可以切换飞行状态
        /// </summary>
        const int FrameCountForFalling = 5;
        /// <summary>
        /// 第一人称视口角度限制
        /// </summary>
        readonly Vector2 LimitViewAngle = new Vector2(-80, 80);


        bool _useRootMotion;          // Animator Room Motion
        Vector3 _velocity;            // 角色控制器速度
        float _crouchSpeed;           // 卧姿速度
        bool _isSprint;       // 是否奔跑中
        bool _isCrouch;       // 是否蹲伏中
        bool _isRoll;         // 是否翻滚中
        float _zoom;                // 视角远近拉升
        float _height;              // 角色身高
        Vector3 _conter;            // 角色控制器 中心点
        float _angleY;              // 角色方向
        bool _isGrounded;           // 是否接触地面
        bool _isJumping;            // 是否跳跃中
        int _fallingFrame;          // 没有接触地面帧数

        private void Awake()
        {
            // 初始化字段
            controller = GetComponent<CharacterController>();
            input = GetComponentInParent<PlayerInput>();
            animator = GetComponent<Animator>();
            audioEngine = GetComponent<AudioSource>();

            // 事件绑定
            input.ECrouchStart += Input_ECrouchStart;
            input.ECrouchEnd += Input_ECrouchEnd;
            input.EViewToggle += Input_EViewToggle;
            input.EJump += Input_Jump;
            input.ERoll += Input_Roll;
            input.ESprintStart += Input_ESprintStart;
            input.ESprintEnd += Input_ESprintEnd;
        }
        private void Start()
        {
            audioEngine.loop = true;
            audioEngine.clip = moveClip;

            _zoom = thirdVirCam.m_YAxis.Value;
            _height = controller.height;
            _conter = controller.center;
            _isGrounded = controller.isGrounded;

            PlayAnimation(GetMoveStateName);
            transform.GetComponentsInChildren<SkinnedMeshRenderer>(true, skinnedMeshes);
            transform.GetComponentsInChildren<MeshRenderer>(true, meshes);
            ToggleView(isThirdPerson);
        }
        private void OnDestroy()
        {
            input.ECrouchStart -= Input_ECrouchStart;
            input.ECrouchEnd -= Input_ECrouchEnd;
            input.EJump -= Input_Jump;
            input.ERoll -= Input_Roll;
            input.ESprintStart -= Input_ESprintStart;
            input.ESprintEnd -= Input_ESprintEnd;
        }

        #region 事件
        /// <summary>
        /// 开始奔跑
        /// </summary>
        private void Input_ESprintStart()
        {
            _isSprint = true;
        }
        /// <summary>
        /// 结束奔跑
        /// </summary>
        private void Input_ESprintEnd()
        {
            _isSprint = false;
        }
        /// <summary>
        /// 人称切换
        /// </summary>
        private void Input_EViewToggle()
        {
            isThirdPerson = !isThirdPerson;
            ToggleView(isThirdPerson);
        }
        /// <summary>
        /// 开始蹲伏
        /// </summary>
        private void Input_ECrouchStart()
        {
            if (_isGrounded && !_isRoll)
            {
                _isCrouch = true;
                PlayAnimation("Crouch");
                controller.height = _height / 2;
                controller.center = _conter - Vector3.up * _conter.y * 0.5f;
            }
        }
        /// <summary>
        /// 结束蹲伏
        /// </summary>
        private void Input_ECrouchEnd()
        {
            if (_isCrouch)
            {
                _isCrouch = false;
                if (IsCanStand)
                {
                    PlayAnimation(GetMoveStateName);
                    controller.height = _height;
                    controller.center = _conter;
                }
            }
        }
        /// <summary>
        /// 开始翻转
        /// </summary>
        private void Input_Roll()
        {
            if (_isGrounded && !_isCrouch)
            {
                PlayAudioClip(rollClip);
                controller.height = _height / 2;
                controller.center = _conter - Vector3.up * _conter.y * 0.5f;
                PlayAnimation("Roll");
                _isRoll = true;
                _useRootMotion = true;
            }
        }
        /// <summary>
        /// 跳跃
        /// </summary>
        private void Input_Jump()
        {
            if (controller.isGrounded && !_isCrouch && !_isRoll && IsCanStand)
            {
                PlayAudioClip(jumpClip);
                _velocity.y = Mathf.Sqrt(JumpHeight * -2f * Gravity);
                _isJumping = true;
                PlayAnimation("Air.Jump");
            }
        }
        private void OnControllerColliderHit(ControllerColliderHit hit)
        {
            //Debug.Log("-------------------------");
            //Debug.Log(hit.gameObject);
        }
        /// <summary>
        /// 与地面接触 开始接触 离开地面
        /// </summary>
        /// <param name="isGround">是否与地面接触</param>
        private void OnToggleGround(bool isGround)
        {
            if (isGround)
            {
                if (_isJumping)
                {
                    PlayAudioClip(fallClip);
                }
                _isJumping = false;
                PlayAnimation(GetMoveStateName);
            }
            else
            {
                // 自由下落
                if (!_isJumping)
                {
                    StartCoroutine(WaitFrameFunc(FrameCountForFalling, () =>
                    {
                        if (!_isGrounded)
                        {
                            _isJumping = true;
                            PlayAnimation("Air.Falling");
                        }
                    }));
                }
            }
        }
        /// <summary>
        /// 等待一段时间执行函数
        /// </summary>
        /// <param name="count"></param>
        /// <param name="action"></param>
        /// <returns></returns>
        private IEnumerator WaitFrameFunc(int count, Action action)
        {
            while (count > 0)
            {
                yield return null;
                count--;
            }
            action?.Invoke();
        }
        private void OnAnimationEnd(int hashName)
        {
            if (hashName == StringToHash("Roll"))
            {
                _useRootMotion = false;
                _isRoll = false;
                if (IsCanStand)
                {
                    if (_isGrounded)
                    {
                        PlayAnimation(GetMoveStateName);
                    }
                    else
                    {
                        _isJumping = true;
                        PlayAnimation("Air.Falling");
                    }
                    controller.height = _height;
                    controller.center = _conter;
                }
                else
                {
                    PlayAnimation("Crouch");
                    controller.height = _height / 2;
                    controller.center = _conter - Vector3.up * _conter.y * 0.5f;
                }
            }
        }
        #endregion

        #region Private Function

        /// <summary>
        /// 重力控制
        /// </summary>
        private void GravityControl()
        {
            if (controller.isGrounded)
            {
                // stop our velocity dropping infinitely when grounded
                if (_velocity.y < 2.0f)
                {
                    _velocity.y = -5f;
                }
            }
            _velocity.y += Gravity * Time.deltaTime;
        }
        /// <summary>
        /// 人称切换
        /// </summary>
        private void ToggleView(bool isThird)
        {
            firstVirCam.Priority = isThird ? 0 : 10;
            thirdVirCam.Priority = isThird ? 10 : 0;
            if (FirstPersonHideSkinned)
            {
                foreach (var item in skinnedMeshes)
                {
                    item.enabled = isThird;
                }
                foreach (var item in meshes)
                {
                    item.enabled = isThird;
                }
            }
        }
        /// <summary>
        /// 控制角度范围 (-108 ~ 180)
        /// </summary>
        private float ClampAngle(float angle)
        {
            angle %= 360;
            if (angle > 180)
            {
                angle -= 360;
            }
            else if (angle < -180)
            {
                angle += 360;
            }
            return angle;
        }
        /// <summary>
        /// 角色是否可以站立，角色上方是否有障碍物
        /// </summary>
        /// <returns></returns>
        private bool IsCanStand
        {
            get
            {
                RaycastHit hit;
                Ray ray = new Ray(transform.position, Vector3.up);
                return !Physics.SphereCast(ray, controller.radius, out hit, _height, obstacleLayer, QueryTriggerInteraction.Ignore);
            }
        }
        /// <summary>
        /// 获取移动状态名称
        /// </summary>
        private string GetMoveStateName
        {
            get
            {
                return isThirdPerson ? "ThirdMove" : "FirstMove";
            }
        }
        /// <summary>
        /// 播放动画
        /// </summary>
        private void PlayAnimation(string name)
        {
            animator.Play(name);
        }
        /// <summary>
        /// 播放音效
        /// </summary>
        /// <param name="clip"></param>
        private void PlayAudioClip(AudioClip clip)
        {
            if (clip)
            {
                AudioSource.PlayClipAtPoint(clip, transform.position);
            }
        }
        /// <summary>
        /// 播放移动音效
        /// </summary>
        private void PlayMoveClip(bool isPlay, float speed)
        {
            if (isPlay)
            {
                if (!audioEngine.isPlaying)
                {
                    audioEngine.Play();
                }
            }
            else
            {
                if (audioEngine.isPlaying)
                {
                    audioEngine.Pause();
                }
            }
            audioEngine.pitch = speed;
        }
        #endregion

        private void OnAnimatorIK(int layerIndex)
        {

        }
        private void OnAnimatorMove()
        {
            if (_useRootMotion)
            {
                Vector3 delta = animator.deltaPosition * 1;
                delta.y = _velocity.y * Time.deltaTime;
                if (controller.enabled)
                {
                    controller.Move(delta);
                }
                else
                {
                    animator.ApplyBuiltinRootMotion();
                }
                transform.rotation *= animator.deltaRotation;
            }
        }
        /// <summary>
        /// 获取Animator状态哈希值
        /// </summary>
        /// <param name="state"></param>
        /// <returns></returns>
        private int StringToHash(string state)
        {
            // 层级+点+状态
            // 层级+点+子状态+点+状态
            // LayerName+"."+StateName[StateMachine+"."+StateName]
            state = animator.GetLayerName(0) + "." + state;
            return Animator.StringToHash(state);
        }
        private void Update()
        {
            _fallingFrame = _isGrounded ? 0 : ++_fallingFrame;
            // 速度控制
            GravityControl();
            Vector2 delta = input.GetMoveValue.normalized;
            // 奔跑大于蹲伏优先级
            float acce = (_isSprint ? (_isCrouch ? 1 : Accelerated) : (_isCrouch ? 0.5f : 1)) * NormalSpeed;

            if (IsCanStand)
            {
                if (controller.isGrounded && !_isRoll && !_isCrouch)
                {
                    PlayAnimation(GetMoveStateName);
                    controller.height = _height;
                    controller.center = _conter;
                }
            }
            else
            {
                if (!_isRoll && !_isJumping)
                {
                    acce = (_isSprint ? 1 : 0.5f) * NormalSpeed;
                    PlayAnimation("Crouch");
                    controller.height = _height / 2;
                    controller.center = _conter - Vector3.up * _conter.y * 0.5f;
                }
            }

            if (isThirdPerson)
            {
                // 相机远近
                _zoom = Mathf.Clamp01(_zoom + input.GetZoomValue);

                // 虚拟相机
                thirdVirCam.m_YAxis.Value = Mathf.MoveTowards(thirdVirCam.m_YAxis.Value, _zoom, Time.deltaTime);
                thirdVirCam.m_XAxis.Value = ClampAngle(thirdVirCam.m_XAxis.Value + input.GetLookValue.x);

                if (_isGrounded)
                {
                    // 角色朝向相机
                    if (delta.magnitude > 0.01f)
                    {
                        _angleY = Mathf.Atan2(delta.x, delta.y) * Mathf.Rad2Deg + Camera.main.transform.eulerAngles.y;
                    }
                    // 速度
                    _velocity.x = transform.forward.x * Mathf.Clamp01(delta.magnitude) * acce;
                    _velocity.z = transform.forward.z * Mathf.Clamp01(delta.magnitude) * acce;
                }
                _crouchSpeed = (delta.magnitude > 0.01 ? 1 : 0) * (_isSprint ? 2 : 1);

                animator.SetFloat("CrouchSpeed", _crouchSpeed);
                animator.SetFloat("ThirdMoveSpeed", _crouchSpeed);
                PlayMoveClip(delta.magnitude > 0.5f && _isGrounded, _isSprint ? (Mathf.Abs(_height - controller.height) > 0.2f ? 1.5f : 2) : 1);
            }
            else
            {
                // 虚拟相机
                Vector3 pos = firstVirCam.transform.position;
                pos.y = transform.position.y + controller.height + firstCamYOffset;
                firstVirCam.transform.position = pos;
                float deltaAngle = -1 * Time.deltaTime * firstCamSpeed * input.GetLookValue.y;
                float angle = ClampAngle(firstVirCam.transform.eulerAngles.x);
                if ((angle < LimitViewAngle.x && deltaAngle < 0) || (angle > LimitViewAngle.y && deltaAngle > 0))
                {

                }
                else
                {
                    firstVirCam.transform.Rotate(Vector3.right * deltaAngle);
                }

                _crouchSpeed = delta.magnitude > 0.5f ? 1 : 0;
                //是否可以奔跑 只前进可以切换奔跑
                if (_isSprint && delta.magnitude > 0.8f && delta.y > 0.8f)
                {
                    acce = (_isCrouch ? 1 : (IsCanStand ? Accelerated : 1)) * NormalSpeed;
                    _crouchSpeed = 2;
                    animator.SetFloat("FirstMoveVSpeed", 2);
                }
                else
                {
                    acce = (_isCrouch ? 0.5f : 1) * NormalSpeed;
                    animator.SetFloat("FirstMoveVSpeed", delta.y);
                }
                if (_isGrounded)
                {
                    // 速度
                    _velocity.x = (transform.forward.x * delta.y + transform.right.x * delta.x) * acce;
                    _velocity.z = (transform.forward.z * delta.y + transform.right.z * delta.x) * acce;
                }

                animator.SetFloat("CrouchSpeed", _crouchSpeed);
                animator.SetFloat("FirstMoveHSpeed", delta.x);
                // 脚步声
                PlayMoveClip(_isGrounded && delta.magnitude > 0.5f, acce / NormalSpeed);
            }


            if (!_useRootMotion)
            {
                if (isThirdPerson)
                {
                    transform.rotation = Quaternion.Euler(0, Mathf.MoveTowardsAngle(transform.eulerAngles.y, _angleY, Time.deltaTime * thirdCamSpeed), 0);
                }
                else
                {
                    transform.Rotate(Vector3.up * Time.deltaTime * firstCamSpeed * input.GetLookValue.x);
                }
                controller.Move(_velocity * Time.deltaTime);
            }
        }
        private void LateUpdate()
        {
            if (_isGrounded != controller.isGrounded)
            {
                _isGrounded = controller.isGrounded;
                if (!_isCrouch && !_isRoll)
                {
                    OnToggleGround(_isGrounded);
                }
            }
            if (animator.GetCurrentAnimatorStateInfo(0).normalizedTime >= 1)
            {
                //Debug.Log("----------------------" + animator.GetCurrentAnimatorStateInfo(0).fullPathHash + ":" + StringToHash("Roll"));
                OnAnimationEnd(animator.GetCurrentAnimatorStateInfo(0).fullPathHash);
            }
        }

        //private void OnDrawGizmos()
        //{
        //    //Gizmos.DrawSphere();
        //}
    }
}